package ${package}.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.util.Assert;
import ${package}.utils.MiscUtils;

/**
 * 泛化DAO基类.
 * 
 * 由MyEclipse自动逆向生成的DAO泛化而成.
 * 
 * @param <T> DAO访问的entity类
 * @param <PK> DAO访问的entity的主键类
 * 
 * @see JpaDaoSupport
 * @author calvin
 */
public class GenericDAO<T, PK extends Serializable> extends JpaDaoSupport {

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	private Class<T> entityClass;

	private String className;

	@SuppressWarnings("unchecked")
	public GenericDAO() {
		// 通过范型反射，取得子类中定义的entityClass.
		this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		className = entityClass.getSimpleName();
	}

	public void create(T entity) {
		Assert.notNull(entity);
		logger.info("saving {} instance", className);
		getJpaTemplate().persist(entity);
		logger.info("save successful");
	}

	public T update(T entity) {
		Assert.notNull(entity);
		logger.info("updating {} instance", className);
		T result = getJpaTemplate().merge(entity);
		logger.info("update successful");
		return result;
	}

	public void delete(T entity) {
		Assert.notNull(entity);
		logger.info("deleting {} instance", className);
		getJpaTemplate().remove(entity);
		logger.info("delete successful");
	}

	public void delete(final PK id) {
		Assert.notNull(id);
		logger.info("deleting {} instance", className);
		getJpaTemplate().remove(findById(id));
		logger.info("delete successful");
	}

	/**
	 * @return 如果id不存在,返回Null.
	 */
	public T findById(final PK id) {
		Assert.notNull(id);
		logger.info("finding {} instance with id: {}", className, id);
		return getJpaTemplate().find(entityClass, id);
	}

	/**
	 * 根据属性查找对象.
	 * 
	 * @param propertyName 属性名
	 * @param value 属性值
	 * @param rowStartIdxAndCount 可选的分页参数.第一个参数为起始的记录，第二个参数为返回的对象数量.
	 * eg.
	 * <code>
	 * findByProperty("country","china");      //查找属性值country为china的对象，返回所有对象.
	 * findByProperty("country","china",0,10); //查找属性值country为china的对象，返回第1-10个符合的对象.
	 * </code>
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByProperty(final String propertyName, final Object value, final int... rowStartIdxAndCount) {
		Assert.hasLength(propertyName);
		logger.info("finding {} instance with property: {} , value: {}",
				new Object[] { className, propertyName, value });

		final String queryString = "select model from " + className + " model where model." + propertyName + "= ?1";
		int rowStartIdx = 0;
		int rowCount = 0;
		if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
			rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
			if (rowStartIdxAndCount.length > 1)
				rowCount = Math.max(0, rowStartIdxAndCount[1]);
		}

		return findPaged(queryString, rowStartIdx, rowCount, value);
	}

	/**
	 * 根据属性查找唯一对象.
	 * 
	 * @return 唯一对象.如果不存在符合条件的结果,返回Null,如果有多个对象符合条件，抛出异常.
	 * 
	 * @param propertyName 属性名
	 * @param value 属性值
	 */
	public T findUniqueByProperty(final String propertyName, final Object value) {
		return MiscUtils.uniqueOfList(findByProperty(propertyName, value));
	}

	/**
	 * 获取全部对象.
	 * 
	 * @param rowStartIdxAndCount 可选的分页参数.第一个参数为起始的记录，第二个参数为返回的对象数量.
	 * eg.
	 * <code>
	 * findAll();     //返回所有对象.
	 * findAll(0,10); //返回第1-10个对象.
	 * </code>
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAll(final int... rowStartIdxAndCount) {
		logger.info("finding all {} instances", className);
		final String queryString = "select model from " + className + " model";
		int rowStartIdx = 0;
		int rowCount = 0;
		if (rowStartIdxAndCount != null && rowStartIdxAndCount.length > 0) {
			rowStartIdx = Math.max(0, rowStartIdxAndCount[0]);
			if (rowStartIdxAndCount.length > 1)
				rowCount = Math.max(0, rowStartIdxAndCount[1]);
		}

		return findPaged(queryString, rowStartIdx, rowCount);
	}

	/**
	 * 使用NamedQuery查询. 
	 * 
	 * @param queryName 定义在entity中的查询语句名.
	 * @param values 任意数量的参数.
	 */
	@SuppressWarnings("unchecked")
	public List findByNamedQuery(final String queryName, final Object... values) {
		Assert.hasLength(queryName);
		return getJpaTemplate().findByNamedQuery(queryName, values);
	}

	/**
	 * 使用NamedQuery查询,返回第一个符合条件的对象. 
	 * 
	 * @param queryName 定义在entity中的查询语句名.
	 * @param values 任意数量的参数.
	 * @return 唯一对象.如果不存在符合条件的结果,返回NULL,如果有多个对象符合条件，抛出异常.
	 */
	@SuppressWarnings("unchecked")
	public Object findUniqueByNamedQuery(final String queryName, final Object... values) {
		Assert.hasLength(queryName);
		return MiscUtils.uniqueOfList(getJpaTemplate().findByNamedQuery(queryName, values));
	}

	/**
	 * 使用NamedQuery查询,带分页参数. 
	 * 
	 * @param queryName 定义在entity中的查询语句名.
	 * @param rowStartIdx 起始的记录,如不想设定，可设为-1.
	 * @param rowCount 返回的记录数,如不想设定，可设为-1.
	 * @param values 任意数量的参数.
	 */
	@SuppressWarnings("unchecked")
	public List findPagedByNamedQuery(final String queryName, final int rowStartIdx, final int rowCount,
			final Object... values) {
		Assert.hasLength(queryName);
		return getJpaTemplate().executeFind(new JpaCallback() {
			public Object doInJpa(EntityManager em) throws PersistenceException {
				Query query = em.createNamedQuery(queryName);

				if (values != null)
					for (int i = 0; i < values.length; i++)
						query.setParameter(i + 1, values[i]);

				if (rowStartIdx > 0)
					query.setFirstResult(rowStartIdx);
				if (rowCount > 0)
					query.setMaxResults(rowCount);

				return query.getResultList();
			}
		});
	}

	/**
	 * 直接使用查询语句查询. 
	 * 
	 * @param values 任意数量的参数.
	 */
	@SuppressWarnings("unchecked")
	public List find(final String queryString, final Object... values) {
		Assert.hasLength(queryString);
		return getJpaTemplate().find(queryString, values);
	}

	/**
	 * 直接使用查询语句查询,返回唯一对象
	 * 
	 * @param values 任意数量的参数.
	 * @return 唯一对象.如果不存在符合条件的结果,返回NULL,如果有多个对象符合条件，抛出异常.
	 */
	@SuppressWarnings("unchecked")
	public Object findUnique(final String queryString, final Object... values) {
		Assert.hasLength(queryString);
		return MiscUtils.uniqueOfList(getJpaTemplate().find(queryString, values));
	}

	/**
	 * 直接使用查询语句查询,带分页参数.
	 * 
	 * @param rowStartIdx 起始的记录,如不想设定，可设为-1.
	 * @param rowCount 返回的记录数,如不想设定，可设为-1.
	 * @param values 任意数量的参数.
	 */
	@SuppressWarnings("unchecked")
	public List findPaged(final String queryString, final int rowStartIdx, final int rowCount, final Object... values) {
		return getJpaTemplate().executeFind(new JpaCallback() {
			public Object doInJpa(EntityManager em) throws PersistenceException {
				Query query = em.createQuery(queryString);

				if (values != null)
					for (int i = 0; i < values.length; i++)
						query.setParameter(i + 1, values[i]);

				if (rowStartIdx > 0)
					query.setFirstResult(rowStartIdx);
				if (rowCount > 0)
					query.setMaxResults(rowCount);

				return query.getResultList();
			}
		});
	}

	/**
	 * 取得entity的class.
	 */
	public Class<T> getEntityClass() {
		return entityClass;
	}

}
